Background

As the data scientist, you have been asked to create a report on food-borne illness outbreaks related to Norovirus and Escherichia coli (E. coli). You are given the dataset in Excel format, NORS_20241106.xlsx (Available on Blackboard),

This project uses data from the National Outbreak Reporting System (NORS). NORS is a web-based platform designed to support reporting to the US Centers for Disease Control and Prevention by local, state, and territorial health departments in the United States of all waterborne disease outbreaks and enteric disease outbreaks transmitted by food, contact with environmental sources, infected persons or animals, or unknown modes of transmission. A dashboard is available from the CDC at https://www.cdc.gov/ncezid/dfwed/beam-dashboard.html and the data was downloaded from https://data.cdc.gov/Foodborne-Waterborne-and-Related-Diseases/NORS/5xkq-dg7x/about_data on November 6, 2024.

If you do not like food-borne diseases (come on! No one wants to get sick from a delicious cookie!), you can pretend the data are on anything. This application applies to any situation where you are asked to explore a dataset with quantitative measurements on three different types of items. In this specific case, it is enteric illnesses.

Loads all necessary libraries and init global variables

# Load the libraries
library(dplyr)         # Work with data frames
library(lubridate)     # Work with date & time
library(stringr)       # Work with strings
library(ggplot2)       # Plot graphs
library(ggwordcloud)   # Plot word cloud
library(plotly)        # Plot choropleth maps
library(tidyr)         # Work with data
library(readxl)        # Read excel files
# Create colors
z_300 <- "#d4d4d8"
z_500 <- "#71717a"
r_100 <- "#fee2e2"
r_500 <- "#ef4444"
r_700 <- "#b91c1c"
r_900 <- "#7f1d1d"
b_100 <- "#dbeafe"
b_500 <- "#3b82f6"
b_700 <- "#1d4ed8"
b_900 <- "#1e3a8a"

Reads the data

# Read the data, the data file must be in the same folder with the source file
df <- read_excel('NORS_20241106.xlsx', sheet = 'NORS_20241106')

Describes the data

Views a glimpse of data

# Use glimpse() to view the structure of the data
glimpse(df)
Rows: 62,415
Columns: 19
$ Year                           <dbl> 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2009, 2010, 2010, 2010, 2010, 2011, 2010, 2010, 2011, 20…
$ Month                          <dbl> 1, 1, 10, 1, 12, 4, 6, 8, 8, 4, 4, 12, 1, 12, 12, 1, 3, 12, 1, 3, 8, 5, 5, 6, 3, 7, 11, 8, 12, 12,…
$ State                          <chr> "Alaska", "Iowa", "Kansas", "North Dakota", "Ohio", "West Virginia", "Nevada", "Michigan", "Oregon…
$ `Primary Mode`                 <chr> "Person-to-person", "Person-to-person", "Food", "Person-to-person", "Food", "Person-to-person", "F…
$ Etiology                       <chr> "Norovirus unknown", "Norovirus unknown", "Norovirus unknown", NA, "Clostridium perfringens", NA, …
$ `Serotype or Genotype`         <chr> NA, NA, NA, NA, NA, NA, NA, NA, "O157:H7", NA, NA, "unknown", NA, NA, NA, NA, NA, NA, NA, "unknown…
$ `Etiology Status`              <chr> "Confirmed", "Confirmed", "Suspected", NA, "Confirmed", NA, "Confirmed", NA, "Confirmed", "Confirm…
$ Setting                        <chr> NA, "Restaurant", "Restaurant: Sit-down dining", "Long-term care/nursing home/assisted living faci…
$ Illnesses                      <dbl> 6, 56, 4, 77, 9, 59, 44, 33, 3, 13, 19, 36, 40, 68, 39, 35, 69, 5, 46, 9, 26, 24, 55, 2, 23, 16, 6…
$ Hospitalizations               <dbl> 0, 2, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2,…
$ `Info On Hospitalizations`     <dbl> 6, 56, 4, 57, 9, 59, 44, 33, 3, 0, 19, 36, 40, 68, 39, 35, 69, 5, 0, 9, 26, 0, 55, 2, 23, 0, 6, 13…
$ Deaths                         <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
$ `Info On Deaths`               <dbl> 6, 56, 4, 57, 9, 59, 44, 33, 3, 13, 19, 36, 40, 68, 39, 35, 69, 5, 46, 9, 26, 0, 55, 2, 23, 0, 6, …
$ `Food Vehicle`                 <chr> NA, NA, NA, NA, "roast beef", NA, NA, "pork", NA, "beans, baked;pork, bbq", "ice cream cake", NA, …
$ `Food Contaminated Ingredient` <chr> NA, NA, NA, NA, NA, NA, NA, "pork", NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ `IFSAC Category`               <chr> NA, NA, NA, NA, "Beef", NA, NA, "Pork", NA, "Multiple", "Multiple", NA, NA, NA, NA, NA, NA, NA, NA…
$ `Water Exposure`               <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ `Water Type`                   <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…
$ `Animal Type`                  <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA…

Views data summary

# View data summary
summary(df)
      Year          Month           State           Primary Mode         Etiology         Serotype or Genotype Etiology Status   
 Min.   :1971   Min.   : 1.000   Length:62415       Length:62415       Length:62415       Length:62415         Length:62415      
 1st Qu.:2009   1st Qu.: 2.000   Class :character   Class :character   Class :character   Class :character     Class :character  
 Median :2013   Median : 5.000   Mode  :character   Mode  :character   Mode  :character   Mode  :character     Mode  :character  
 Mean   :2012   Mean   : 5.706                                                                                                   
 3rd Qu.:2017   3rd Qu.: 9.000                                                                                                   
 Max.   :2022   Max.   :12.000                                                                                                   
                                                                                                                                 
   Setting            Illnesses      Hospitalizations  Info On Hospitalizations     Deaths       Info On Deaths    Food Vehicle      
 Length:62415       Min.   :     2   Min.   :  0.000   Min.   :   0.00          Min.   : 0.000   Min.   :   0.00   Length:62415      
 Class :character   1st Qu.:     5   1st Qu.:  0.000   1st Qu.:   3.00          1st Qu.: 0.000   1st Qu.:   4.00   Class :character  
 Mode  :character   Median :    14   Median :  0.000   Median :  10.00          Median : 0.000   Median :  11.00   Mode  :character  
                    Mean   :    34   Mean   :  0.821   Mean   :  19.89          Mean   : 0.043   Mean   :  20.75                     
                    3rd Qu.:    30   3rd Qu.:  1.000   3rd Qu.:  25.00          3rd Qu.: 0.000   3rd Qu.:  26.00                     
                    Max.   :403000   Max.   :308.000   Max.   :2500.00          Max.   :50.000   Max.   :2500.00                     
                                     NA's   :8215      NA's   :8114             NA's   :7502     NA's   :8124                        
 Food Contaminated Ingredient IFSAC Category     Water Exposure      Water Type        Animal Type       
 Length:62415                 Length:62415       Length:62415       Length:62415       Length:62415      
 Class :character             Class :character   Class :character   Class :character   Class :character  
 Mode  :character             Mode  :character   Mode  :character   Mode  :character   Mode  :character  
                                                                                                         
                                                                                                         
                                                                                                         
                                                                                                         

Views data dimension

# View data dimension
dim(df)
[1] 62415    19

There are 62415 rows and 19 columns.

Views the column names

# View the column names
colnames(df)
 [1] "Year"                         "Month"                        "State"                        "Primary Mode"                
 [5] "Etiology"                     "Serotype or Genotype"         "Etiology Status"              "Setting"                     
 [9] "Illnesses"                    "Hospitalizations"             "Info On Hospitalizations"     "Deaths"                      
[13] "Info On Deaths"               "Food Vehicle"                 "Food Contaminated Ingredient" "IFSAC Category"              
[17] "Water Exposure"               "Water Type"                   "Animal Type"                 

Remove white spaces in column names by renaming them:

df <- df %>%
  rename(
    "Primary_Mode"                 = `Primary Mode`,
    "Serotype_Or_Genotype"         = `Serotype or Genotype`,
    "Etiology_Status"              = `Etiology Status`,
    "Info_On_Hospitalizations"     = `Info On Hospitalizations`,
    "Info_On_Deaths"               = `Info On Deaths`,
    "Food_Vehicle"                 = `Food Vehicle`,
    "Food_Contaminated_Ingredient" = `Food Contaminated Ingredient`,
    "IFSAC_Category"               = `IFSAC Category`,
    "Water_Exposure"               = `Water Exposure`,
    "Water_Type"                   = `Water Type`,
    "Animal_Type"                  = `Animal Type`
  )

View the top 10 rows

# View the first 10 rows
head(df, n = 10)

View the last 10 rows

# View the last 10 rows
tail(df, n = 10)

Explores and cleans the data

Checks for NAs or blanks.

# Count the number of rows in the data frame that contain NA values
paste0(nrow(df[!complete.cases(df), ]), "/", nrow(df))
[1] "62415/62415"

The result shows that all rows have at least one column containing NA.

Do you need to take appropriate action to address the missing values? Explain why you believe the NAs should be there

Based on the FAQ on the website, here are some reasons that NAs exist in the dataset:

  • NORS collects information only on disease outbreaks of enteric illness, except for some non-enteric illness outbreaks spread through contaminated food or water.
  • NORS does not collect information on disease outbreaks that result from an exposure in a non-U.S. location.
  • Health departments might not report all foodborne, waterborne, or enteric disease outbreaks to NORS.
    • Some outbreaks are never identified.
    • Some outbreaks are not investigated.
    • Some outbreak investigations cannot be completed.
  • The NORS View contains only information reported to NORS that CDC has reviewed.

Check for NAs or blanks, or both NAs and blanks in each column:

# For each column
for (column_name in colnames(df))
{
  # Get column data
  # Ref: https://www.geeksforgeeks.org/difference-between-single-and-double-square-brackets-in-r/
  column_data <- df[[column_name]]
  has_NAs <- any(is.na(column_data))
  has_blanks <- any(column_data == "", na.rm = TRUE)
  
  # If the data contains NAs and blanks
  if (any(has_NAs) & any(has_blanks))
  {
    cat("Column", column_name,"has NAs and blanks as missing values\n")
  }

  # If the data contains only blanks
  if (any(has_blanks))
  {
    cat("Column", column_name, "has only blanks as missing values\n")
  }

  # If the data contains only NAs
  if (any(has_NAs))
  {
    cat("Column", column_name, "has only NAs as missing values\n")
  }
}
Column Etiology has only NAs as missing values
Column Serotype_Or_Genotype has only NAs as missing values
Column Etiology_Status has only NAs as missing values
Column Setting has only NAs as missing values
Column Hospitalizations has only NAs as missing values
Column Info_On_Hospitalizations has only NAs as missing values
Column Deaths has only NAs as missing values
Column Info_On_Deaths has only NAs as missing values
Column Food_Vehicle has only NAs as missing values
Column Food_Contaminated_Ingredient has only NAs as missing values
Column IFSAC_Category has only NAs as missing values
Column Water_Exposure has only NAs as missing values
Column Water_Type has only NAs as missing values
Column Animal_Type has only NAs as missing values

There are also some columns have data depend on a value from other columns. For examples, Food Vehicle data is only for foodborne outbreaks only. Besides, removing rows having NAs or blanks in this dataset will also reduce the overall statistic

Image was obtained from: https://www.cdc.gov/ncezid/dfwed/beam-dashboard.html (NORS view dashboard)

In conclusion, NAs and blanks can be kept in the dataset.

Create a new column called Date to combine year and month

# Create a new column Date to combine year and month as Datetime datatype
df$Date <- lubridate::ymd(paste(df$Year, df$Month, "01", sep = "-"))

Check column data type:

# Check data type of Date column
class(df$Date)
[1] "Date"

Select all the rows which have Norovirus as substring

# Filter rows contain Norovirus as substring
norovirus_df <- df %>%
  filter(str_detect(df$Etiology, "Norovirus"))

Present statistics on Norovirus that you think would be of interest to your project’s sponsor

Which primary mode has the highest number of Norovirus outbreaks?

# Group data by Primary_Mode column and count the number of occurences
norovirus_by_primary_mode <- norovirus_df %>% 
  count(Primary_Mode)

norovirus_by_primary_mode

Result: Person-to-person is the main reason of Norovirus outbreaks, with 21214 cases.

Where are the top 3 places that Norovirus outbreaks occur most frequently when the primary mode is Person-to-person?

person_to_person_by_setting <- norovirus_df %>% 
  # Select Person-to-person as the primary mode and filter out NAs from Setting column
  filter(Primary_Mode == "Person-to-person", !is.na(Setting))

norovirus_by_places <- person_to_person_by_setting %>%
  # Group data by Setting column and count the number of occurences
  count(Setting) %>%
  filter(n > 0) %>%
  arrange(desc(norovirus_by_places$n))

norovirus_by_places 

Result: Top 3 places that easily transmit the Norovirus are Child daycare/preschool, Hospital, and Long-term care/nursing home/assisted living facility.

Create graphs to visualize the statistics of Norovirus

Which primary mode has the highest number of Norovirus outbreaks?

# Create a horizontal bar chart
ggplot(
  norovirus_by_primary_mode, 
  aes(x = n, y = Primary_Mode)
) +
  geom_bar(
    stat = "identity",
    aes(fill = ifelse(n == max(n), r_900, z_300))
  ) + 
  geom_text(
    # Display text for only the bar with highest number of occurrences
    aes(label = ifelse(n == max(n), n, "")),
    color = "white",
    hjust = 1.35,
    vjust = 0.35
  ) +
  # Enable custom fill color
  scale_fill_identity() +
  labs(
    title = "Norovirus outbreaks by primary mode",
    x = "Outbreaks"
  ) +
  # Adjust theme for a better look
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold"),
    axis.title.x = element_text(),
    axis.title.y = element_blank(),
    axis.text.y = element_text(),
    axis.line.y = element_blank(),
    axis.ticks.y = element_blank()
  )

Where are the top 3 places that Norovirus outbreaks occur most frequently when the primary mode is Person-to-person?

is_top_3_places <- function(setting) {
  return(setting %in% head(norovirus_by_places, 3)$Setting
  )
}

ggplot(
  norovirus_by_places,
  # x-axis is the number of occurrences, y-axis is sorted by descending of n
  aes(x = n, y = reorder(Setting, n))
) +
  geom_bar(
    stat = "identity",
    aes(fill = ifelse(is_top_3_places(Setting), r_900, z_300))
  ) +
  geom_text( # size in pt
    aes(label = ifelse(is_top_3_places(Setting), n, "")),
    color = "white",
    hjust = 1.35,
    vjust = 0.35
  ) +
  scale_fill_identity() +
  labs(
    title = "Top 3 locations for Norovirus outbreaks via Person-to-person transmission",
    x = "Outbreaks"
  ) +
  # Adjust theme for a better look
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold"),
    axis.title.x = element_text(),
    axis.title.y = element_blank(),
    axis.text.y = element_text(),
    axis.line.y = element_blank(),
    axis.ticks.y = element_blank()
  )

Select all the rows which have Escherichia as substring

# Filter rows contain Escherichia as substring
escherichia_coli_df <- df %>%
  filter(str_detect(df$Etiology, "Escherichia"))

Determine how to handle instances where multiple etiologies are listed

Instances with multiple etiologies can be split into multiple rows. However, this approach is only suitable for data visualization scenarios when only a few columns are selected to plot a graph. It cannot be applied to the entire dataset because some values, like the number of illnesses, hospitalizations, and deaths, cannot be split.

Present statistics on Escherichia that you think would be of interest to your project’s sponsor

Which primary mode has the highest number of Escherichia outbreaks?

# Group data by Primary_Mode column and count the number of occurences
escherichia_coli_by_primary_mode <- escherichia_coli_df %>% 
  count(Primary_Mode)

escherichia_coli_by_primary_mode

Result: Food is the main reason of Escherichia outbreaks, with 784 cases.

Which food vehicle is the most frequently associated with Escherichia outbreaks when the primary mode is Food?

escherichia_coli_food_vehicles <- escherichia_coli_df %>% 
  # Select Food as the primary mode and filter out NAs from Food Vehicle column
  filter(Primary_Mode == "Food", !is.na(Food_Vehicle)) %>%
  # Split multiple food vehicles into separate rows, with delimiter is semicolon ';'
  separate_longer_delim(Food_Vehicle, delim = ";") %>%
  # Group data by Setting column and count the number of occurences
  count(Food_Vehicle) %>%
  # Sort by descending of n
  arrange(desc(n))

escherichia_coli_food_vehicles

Result: ground beef, hamburger is the most frequently associated with Escherichia outbreaks when the primary mode is Food.

Create graphs to visualize the statistics of Escherichia

Which primary mode has the highest number of Escherichia outbreaks?

# Create a horizontal bar chart
ggplot(
  escherichia_coli_by_primary_mode, 
  aes(x = n, y = Primary_Mode)
) +
  geom_bar(
    stat = "identity",
    aes(fill = ifelse(n == max(n), r_900, z_300))
  ) + 
  geom_text(
    # Display text for only the bar with highest number of occurrences
    aes(label = ifelse(n == max(n), n, "")),
    color = "white",
    hjust = 1.35,
    vjust = 0.35
  ) +
  # Enable custom fill color
  scale_fill_identity() +
  labs(
    title = "Escherichia outbreaks by primary mode",
    x = "Outbreaks"
  ) +
  # Adjust theme for a better look
  theme_classic() +
  theme(
    plot.title = element_text(face = "bold"),
    axis.title.x = element_text(),
    axis.title.y = element_blank(),
    axis.text.y = element_text(),
    axis.line.y = element_blank(),
    axis.ticks.y = element_blank()
  )

Which food vehicle is the most frequently associated with Escherichia outbreaks when the primary mode is Food?

ggplot(
  escherichia_coli_food_vehicles, 
  aes(label = Food_Vehicle, size = n, color = n)) +
  geom_text_wordcloud() +
  scale_size_area(max_size = 30) +
  theme_minimal() +
  scale_color_gradient(low = z_500, high = r_900)

Notice that top 3 words are having ground beef as substring in the graph.

Select at least one statistic and present a graph showing both Escherichia and Norovirus together

split_etiology <- df %>%
  # Filter Etiology without NAs
  filter(!is.na(Etiology)) %>%
  # Split multiple Etiology into separate rows, with delimiter is semicolon ';'
  separate_longer_delim(Etiology, delim = ";")

# Rename the etiologies for grouping later
split_etiology$Etiology <- str_replace(split_etiology$Etiology, ".*Norovirus.*", "Norovirus")
split_etiology$Etiology <- str_replace(split_etiology$Etiology, ".*Escherichia.*", "Escherichia")

# Add state code column for plotting choropleth map
split_etiology$State_Code <- state.abb[match(split_etiology$State, state.name)]

# Filter Norovirus and Escherichia etiologies only
norovirus_and_escherichia <- split_etiology %>%
  filter(Etiology == "Norovirus" | Etiology == "Escherichia")

The total number of sickened (illnesses, hospitalizations & deaths) of Norovirus and Escherichia etiologies by year

norovirus_and_escherichia_by_year <- norovirus_and_escherichia %>%
  # Select Year and Etiology
  select(Year, Etiology, Illnesses, Hospitalizations, Deaths) %>%
  group_by(Year, Etiology) %>%
  summarize(
    # Calculate sum of illnesses, hospitalizations & deaths, ignoring NAs
    Total_Sickened = sum(Illnesses + Hospitalizations + Deaths, na.rm = TRUE)
  ) %>%
  filter(Total_Sickened > 0)
`summarise()` has grouped output by 'Year'. You can override using the `.groups` argument.
norovirus_and_escherichia_by_year
ggplot(
  norovirus_and_escherichia_by_year, 
  aes(x = Year, y = Total_Sickened, 
      color = Etiology,
      group = Etiology
  )
) +
  geom_line(size = 1) +
  geom_point(size = 3, aes(shape=Etiology)) +
  scale_color_manual(values = c("Norovirus" = r_700, "Escherichia" = b_700)) +
  labs(
    title = "Total number of sickened (illnesses, hospitalizations & deaths) of Norovirus and Escherichia etiologies by year",
    x = "Year",
    y = "Total number of sickened"
  ) +
  theme_classic()

Norovirus and Escherichia etiologies outbreaks by state

norovirus_and_escherichia_by_state <- norovirus_and_escherichia %>%
  # Select Year and Etiology
  select(State, State_Code, Etiology, Illnesses, Hospitalizations, Deaths) %>%
  group_by(State_Code, Etiology) %>%
  summarize(
    # Calculate sum of illnesses, hospitalizations & deaths, ignoring NAs
    Total_Sickened = sum(Illnesses + Hospitalizations + Deaths, na.rm = TRUE)
  ) %>%
  filter(Total_Sickened > 0)
`summarise()` has grouped output by 'State_Code'. You can override using the `.groups` argument.
norovirus_and_escherichia_by_state
norovirus_and_escherichia_by_state %>%
  filter(Etiology == "Norovirus") %>%
  plot_geo(locationmode = 'USA-states') %>%
  # Build the map
  add_trace(
    type="choropleth",
    z = ~Total_Sickened,
    locations = ~State_Code,
    color     = ~Total_Sickened,
    colorscale = list(
      # Low level
      c(0, "#fee2e2"),
      c(0.5, "#ef4444"),
      c(1, r_900)
    )
  ) %>%
  # https://plotly.com/r/reference/layout/geo
  layout(
    title = 'Norovirus etiologies outbreaks by state',
    geo = list(
      # Show USA instead of the world map
      scope      = "usa",
      # Map data across the states
      projection = list(type = "albers usa")
    )
  ) %>%
  # Color bar title
  colorbar(title = "Total number of sickened")
norovirus_and_escherichia_by_state %>%
  filter(Etiology == "Escherichia") %>%
  plot_geo(locationmode = 'USA-states') %>%
  # Build the map
  add_trace(
    type="choropleth",
    z = ~Total_Sickened,
    locations = ~State_Code,
    color     = ~Total_Sickened,
    colorscale = list(
      # Low level
      c(0, b_100),
      c(0.5, b_500),
      c(1, b_900)
    )
  ) %>%
  # https://plotly.com/r/reference/layout/geo
  layout(
    title = 'Escherichia etiologies outbreaks by state',
    geo = list(
      # Show USA instead of the world map
      scope      = "usa",
      # Map data across the states
      projection = list(type = "albers usa")
    )
  ) %>%
  # Color bar title
  colorbar(title = "Total number of sickened")
LS0tDQp0aXRsZTogIlIgTWluaSBQcm9qZWN0IDQ6IFIgU2NyaXB0IENvdmVyaW5nIE91dGxpZXJzIGFuZCBHcmFwaHMiDQphdXRob3I6ICJUaGluaCBMZSINCmRhdGU6ICIyMDI0LTExLTA4Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBCYWNrZ3JvdW5kDQoNCkFzIHRoZSBkYXRhIHNjaWVudGlzdCwgeW91IGhhdmUgYmVlbiBhc2tlZCB0byBjcmVhdGUgYSByZXBvcnQgb24gZm9vZC1ib3JuZSBpbGxuZXNzIG91dGJyZWFrcyByZWxhdGVkIHRvIE5vcm92aXJ1cyBhbmQgRXNjaGVyaWNoaWEgY29saSAoRS4gY29saSkuIFlvdSBhcmUgZ2l2ZW4gdGhlIGRhdGFzZXQgaW4gRXhjZWwgZm9ybWF0LCBOT1JTXzIwMjQxMTA2Lnhsc3ggKEF2YWlsYWJsZSBvbiBCbGFja2JvYXJkKSwNCg0KVGhpcyBwcm9qZWN0IHVzZXMgZGF0YSBmcm9tIHRoZSBOYXRpb25hbCBPdXRicmVhayBSZXBvcnRpbmcgU3lzdGVtIChOT1JTKS4gTk9SUyBpcyBhIHdlYi1iYXNlZCBwbGF0Zm9ybSBkZXNpZ25lZCB0byBzdXBwb3J0IHJlcG9ydGluZyB0byB0aGUgVVMgQ2VudGVycyBmb3IgRGlzZWFzZSBDb250cm9sIGFuZCBQcmV2ZW50aW9uIGJ5IGxvY2FsLCBzdGF0ZSwgYW5kIHRlcnJpdG9yaWFsIGhlYWx0aCBkZXBhcnRtZW50cyBpbiB0aGUgVW5pdGVkIFN0YXRlcyBvZiBhbGwgd2F0ZXJib3JuZSBkaXNlYXNlIG91dGJyZWFrcyBhbmQgZW50ZXJpYyBkaXNlYXNlIG91dGJyZWFrcyB0cmFuc21pdHRlZCBieSBmb29kLCBjb250YWN0IHdpdGggZW52aXJvbm1lbnRhbCBzb3VyY2VzLCBpbmZlY3RlZCBwZXJzb25zIG9yIGFuaW1hbHMsIG9yIHVua25vd24gbW9kZXMgb2YgdHJhbnNtaXNzaW9uLiBBIGRhc2hib2FyZCBpcyBhdmFpbGFibGUgZnJvbSB0aGUgQ0RDIGF0IDxodHRwczovL3d3dy5jZGMuZ292L25jZXppZC9kZndlZC9iZWFtLWRhc2hib2FyZC5odG1sPiBhbmQgdGhlIGRhdGEgd2FzIGRvd25sb2FkZWQgZnJvbSA8aHR0cHM6Ly9kYXRhLmNkYy5nb3YvRm9vZGJvcm5lLVdhdGVyYm9ybmUtYW5kLVJlbGF0ZWQtRGlzZWFzZXMvTk9SUy81eGtxLWRnN3gvYWJvdXRfZGF0YT4gb24gTm92ZW1iZXIgNiwgMjAyNC4NCg0KSWYgeW91IGRvIG5vdCBsaWtlIGZvb2QtYm9ybmUgZGlzZWFzZXMgKGNvbWUgb24hIE5vIG9uZSB3YW50cyB0byBnZXQgc2ljayBmcm9tIGEgZGVsaWNpb3VzIGNvb2tpZSEpLCB5b3UgY2FuIHByZXRlbmQgdGhlIGRhdGEgYXJlIG9uIGFueXRoaW5nLiBUaGlzIGFwcGxpY2F0aW9uIGFwcGxpZXMgdG8gYW55IHNpdHVhdGlvbiB3aGVyZSB5b3UgYXJlIGFza2VkIHRvIGV4cGxvcmUgYSBkYXRhc2V0IHdpdGggcXVhbnRpdGF0aXZlIG1lYXN1cmVtZW50cyBvbiB0aHJlZSBkaWZmZXJlbnQgdHlwZXMgb2YgaXRlbXMuIEluIHRoaXMgc3BlY2lmaWMgY2FzZSwgaXQgaXMgZW50ZXJpYyBpbGxuZXNzZXMuDQoNCiMgTG9hZHMgYWxsIG5lY2Vzc2FyeSBsaWJyYXJpZXMgYW5kIGluaXQgZ2xvYmFsIHZhcmlhYmxlcw0KDQpgYGB7cn0NCiMgTG9hZCB0aGUgbGlicmFyaWVzDQpsaWJyYXJ5KGRwbHlyKSAgICAgICAgICMgV29yayB3aXRoIGRhdGEgZnJhbWVzDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgICAgICMgV29yayB3aXRoIGRhdGUgJiB0aW1lDQpsaWJyYXJ5KHN0cmluZ3IpICAgICAgICMgV29yayB3aXRoIHN0cmluZ3MNCmxpYnJhcnkoZ2dwbG90MikgICAgICAgIyBQbG90IGdyYXBocw0KbGlicmFyeShnZ3dvcmRjbG91ZCkgICAjIFBsb3Qgd29yZCBjbG91ZA0KbGlicmFyeShwbG90bHkpICAgICAgICAjIFBsb3QgY2hvcm9wbGV0aCBtYXBzDQpsaWJyYXJ5KHRpZHlyKSAgICAgICAgICMgV29yayB3aXRoIGRhdGENCmxpYnJhcnkocmVhZHhsKSAgICAgICAgIyBSZWFkIGV4Y2VsIGZpbGVzDQojIENyZWF0ZSBjb2xvcnMNCnpfMzAwIDwtICIjZDRkNGQ4Ig0Kel81MDAgPC0gIiM3MTcxN2EiDQpyXzEwMCA8LSAiI2ZlZTJlMiINCnJfNTAwIDwtICIjZWY0NDQ0Ig0Kcl83MDAgPC0gIiNiOTFjMWMiDQpyXzkwMCA8LSAiIzdmMWQxZCINCmJfMTAwIDwtICIjZGJlYWZlIg0KYl81MDAgPC0gIiMzYjgyZjYiDQpiXzcwMCA8LSAiIzFkNGVkOCINCmJfOTAwIDwtICIjMWUzYThhIg0KYGBgDQoNCiMgUmVhZHMgdGhlIGRhdGENCg0KYGBge3J9DQojIFJlYWQgdGhlIGRhdGEsIHRoZSBkYXRhIGZpbGUgbXVzdCBiZSBpbiB0aGUgc2FtZSBmb2xkZXIgd2l0aCB0aGUgc291cmNlIGZpbGUNCmRmIDwtIHJlYWRfZXhjZWwoJ05PUlNfMjAyNDExMDYueGxzeCcsIHNoZWV0ID0gJ05PUlNfMjAyNDExMDYnKQ0KYGBgDQoNCiMgRGVzY3JpYmVzIHRoZSBkYXRhDQoNCiMjIFZpZXdzIGEgZ2xpbXBzZSBvZiBkYXRhDQoNCmBgYHtyfQ0KIyBVc2UgZ2xpbXBzZSgpIHRvIHZpZXcgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YQ0KZ2xpbXBzZShkZikNCmBgYA0KDQojIyBWaWV3cyBkYXRhIHN1bW1hcnkNCg0KYGBge3J9DQojIFZpZXcgZGF0YSBzdW1tYXJ5DQpzdW1tYXJ5KGRmKQ0KYGBgDQoNCiMjIFZpZXdzIGRhdGEgZGltZW5zaW9uDQoNCmBgYHtyfQ0KIyBWaWV3IGRhdGEgZGltZW5zaW9uDQpkaW0oZGYpDQpgYGANCg0KVGhlcmUgYXJlIDYyNDE1IHJvd3MgYW5kIDE5IGNvbHVtbnMuDQoNCiMjIFZpZXdzIHRoZSBjb2x1bW4gbmFtZXMNCg0KYGBge3J9DQojIFZpZXcgdGhlIGNvbHVtbiBuYW1lcw0KY29sbmFtZXMoZGYpDQpgYGANCg0KUmVtb3ZlIHdoaXRlIHNwYWNlcyBpbiBjb2x1bW4gbmFtZXMgYnkgcmVuYW1pbmcgdGhlbToNCg0KYGBge3J9DQpkZiA8LSBkZiAlPiUNCiAgcmVuYW1lKA0KICAgICJQcmltYXJ5X01vZGUiICAgICAgICAgICAgICAgICA9IGBQcmltYXJ5IE1vZGVgLA0KICAgICJTZXJvdHlwZV9Pcl9HZW5vdHlwZSIgICAgICAgICA9IGBTZXJvdHlwZSBvciBHZW5vdHlwZWAsDQogICAgIkV0aW9sb2d5X1N0YXR1cyIgICAgICAgICAgICAgID0gYEV0aW9sb2d5IFN0YXR1c2AsDQogICAgIkluZm9fT25fSG9zcGl0YWxpemF0aW9ucyIgICAgID0gYEluZm8gT24gSG9zcGl0YWxpemF0aW9uc2AsDQogICAgIkluZm9fT25fRGVhdGhzIiAgICAgICAgICAgICAgID0gYEluZm8gT24gRGVhdGhzYCwNCiAgICAiRm9vZF9WZWhpY2xlIiAgICAgICAgICAgICAgICAgPSBgRm9vZCBWZWhpY2xlYCwNCiAgICAiRm9vZF9Db250YW1pbmF0ZWRfSW5ncmVkaWVudCIgPSBgRm9vZCBDb250YW1pbmF0ZWQgSW5ncmVkaWVudGAsDQogICAgIklGU0FDX0NhdGVnb3J5IiAgICAgICAgICAgICAgID0gYElGU0FDIENhdGVnb3J5YCwNCiAgICAiV2F0ZXJfRXhwb3N1cmUiICAgICAgICAgICAgICAgPSBgV2F0ZXIgRXhwb3N1cmVgLA0KICAgICJXYXRlcl9UeXBlIiAgICAgICAgICAgICAgICAgICA9IGBXYXRlciBUeXBlYCwNCiAgICAiQW5pbWFsX1R5cGUiICAgICAgICAgICAgICAgICAgPSBgQW5pbWFsIFR5cGVgDQogICkNCmBgYA0KDQojIyBWaWV3IHRoZSB0b3AgMTAgcm93cw0KDQpgYGB7cn0NCiMgVmlldyB0aGUgZmlyc3QgMTAgcm93cw0KaGVhZChkZiwgbiA9IDEwKQ0KYGBgDQoNCiMjIFZpZXcgdGhlIGxhc3QgMTAgcm93cw0KDQpgYGB7cn0NCiMgVmlldyB0aGUgbGFzdCAxMCByb3dzDQp0YWlsKGRmLCBuID0gMTApDQpgYGANCg0KIyBFeHBsb3JlcyBhbmQgY2xlYW5zIHRoZSBkYXRhDQoNCiMjIENoZWNrcyBmb3IgTkFzIG9yIGJsYW5rcy4NCg0KYGBge3J9DQojIENvdW50IHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZSB0aGF0IGNvbnRhaW4gTkEgdmFsdWVzDQpwYXN0ZTAobnJvdyhkZlshY29tcGxldGUuY2FzZXMoZGYpLCBdKSwgIi8iLCBucm93KGRmKSkNCmBgYA0KDQpUaGUgcmVzdWx0IHNob3dzIHRoYXQgYWxsIHJvd3MgaGF2ZSBhdCBsZWFzdCBvbmUgY29sdW1uIGNvbnRhaW5pbmcgTkEuDQoNCiMjIERvIHlvdSBuZWVkIHRvIHRha2UgYXBwcm9wcmlhdGUgYWN0aW9uIHRvIGFkZHJlc3MgdGhlIG1pc3NpbmcgdmFsdWVzPyBFeHBsYWluIHdoeSB5b3UgYmVsaWV2ZSB0aGUgTkFzIHNob3VsZCBiZSB0aGVyZQ0KDQpCYXNlZCBvbiB0aGUgRkFRIG9uIHRoZSB3ZWJzaXRlLCBoZXJlIGFyZSBzb21lIHJlYXNvbnMgdGhhdCBOQXMgZXhpc3QgaW4gdGhlIGRhdGFzZXQ6DQoNCj4gLSAgIE5PUlMgY29sbGVjdHMgaW5mb3JtYXRpb24gb25seSBvbiBkaXNlYXNlIG91dGJyZWFrcyBvZiBlbnRlcmljIGlsbG5lc3MsIGV4Y2VwdCBmb3Igc29tZSBub24tZW50ZXJpYyBpbGxuZXNzIG91dGJyZWFrcyBzcHJlYWQgdGhyb3VnaCBjb250YW1pbmF0ZWQgZm9vZCBvciB3YXRlci4NCj4gLSAgIE5PUlMgZG9lcyBub3QgY29sbGVjdCBpbmZvcm1hdGlvbiBvbiBkaXNlYXNlIG91dGJyZWFrcyB0aGF0IHJlc3VsdCBmcm9tIGFuIGV4cG9zdXJlIGluIGEgbm9uLVUuUy4gbG9jYXRpb24uDQo+IC0gICBIZWFsdGggZGVwYXJ0bWVudHMgbWlnaHQgbm90IHJlcG9ydCBhbGwgZm9vZGJvcm5lLCB3YXRlcmJvcm5lLCBvciBlbnRlcmljIGRpc2Vhc2Ugb3V0YnJlYWtzIHRvIE5PUlMuDQo+ICAgICAtICAgU29tZSBvdXRicmVha3MgYXJlIG5ldmVyIGlkZW50aWZpZWQuDQo+ICAgICAtICAgU29tZSBvdXRicmVha3MgYXJlIG5vdCBpbnZlc3RpZ2F0ZWQuDQo+ICAgICAtICAgU29tZSBvdXRicmVhayBpbnZlc3RpZ2F0aW9ucyBjYW5ub3QgYmUgY29tcGxldGVkLg0KPiAtICAgVGhlIE5PUlMgVmlldyBjb250YWlucyBvbmx5IGluZm9ybWF0aW9uIHJlcG9ydGVkIHRvIE5PUlMgdGhhdCBDREMgaGFzIHJldmlld2VkLg0KDQpDaGVjayBmb3IgTkFzIG9yIGJsYW5rcywgb3IgYm90aCBOQXMgYW5kIGJsYW5rcyBpbiBlYWNoIGNvbHVtbjoNCg0KYGBge3J9DQojIEZvciBlYWNoIGNvbHVtbg0KZm9yIChjb2x1bW5fbmFtZSBpbiBjb2xuYW1lcyhkZikpDQp7DQogICMgR2V0IGNvbHVtbiBkYXRhDQogICMgUmVmOiBodHRwczovL3d3dy5nZWVrc2ZvcmdlZWtzLm9yZy9kaWZmZXJlbmNlLWJldHdlZW4tc2luZ2xlLWFuZC1kb3VibGUtc3F1YXJlLWJyYWNrZXRzLWluLXIvDQogIGNvbHVtbl9kYXRhIDwtIGRmW1tjb2x1bW5fbmFtZV1dDQogIGhhc19OQXMgPC0gYW55KGlzLm5hKGNvbHVtbl9kYXRhKSkNCiAgaGFzX2JsYW5rcyA8LSBhbnkoY29sdW1uX2RhdGEgPT0gIiIsIG5hLnJtID0gVFJVRSkNCiAgDQogICMgSWYgdGhlIGRhdGEgY29udGFpbnMgTkFzIGFuZCBibGFua3MNCiAgaWYgKGFueShoYXNfTkFzKSAmIGFueShoYXNfYmxhbmtzKSkNCiAgew0KICAgIGNhdCgiQ29sdW1uIiwgY29sdW1uX25hbWUsImhhcyBOQXMgYW5kIGJsYW5rcyBhcyBtaXNzaW5nIHZhbHVlc1xuIikNCiAgfQ0KDQogICMgSWYgdGhlIGRhdGEgY29udGFpbnMgb25seSBibGFua3MNCiAgaWYgKGFueShoYXNfYmxhbmtzKSkNCiAgew0KICAgIGNhdCgiQ29sdW1uIiwgY29sdW1uX25hbWUsICJoYXMgb25seSBibGFua3MgYXMgbWlzc2luZyB2YWx1ZXNcbiIpDQogIH0NCg0KICAjIElmIHRoZSBkYXRhIGNvbnRhaW5zIG9ubHkgTkFzDQogIGlmIChhbnkoaGFzX05BcykpDQogIHsNCiAgICBjYXQoIkNvbHVtbiIsIGNvbHVtbl9uYW1lLCAiaGFzIG9ubHkgTkFzIGFzIG1pc3NpbmcgdmFsdWVzXG4iKQ0KICB9DQp9DQpgYGANCg0KVGhlcmUgYXJlIGFsc28gc29tZSBjb2x1bW5zIGhhdmUgZGF0YSBkZXBlbmQgb24gYSB2YWx1ZSBmcm9tIG90aGVyIGNvbHVtbnMuIEZvciBleGFtcGxlcywgRm9vZCBWZWhpY2xlIGRhdGEgaXMgb25seSBmb3IgZm9vZGJvcm5lIG91dGJyZWFrcyBvbmx5LiBCZXNpZGVzLCByZW1vdmluZyByb3dzIGhhdmluZyBOQXMgb3IgYmxhbmtzIGluIHRoaXMgZGF0YXNldCB3aWxsIGFsc28gcmVkdWNlIHRoZSBvdmVyYWxsIHN0YXRpc3RpYw0KDQohW10oaW1hZ2VzL2NsaXBib2FyZC0zODQ2MjAxOTg3LnBuZykNCg0KSW1hZ2Ugd2FzIG9idGFpbmVkIGZyb206IDxodHRwczovL3d3dy5jZGMuZ292L25jZXppZC9kZndlZC9iZWFtLWRhc2hib2FyZC5odG1sPiAoTk9SUyB2aWV3IGRhc2hib2FyZCkNCg0KSW4gY29uY2x1c2lvbiwgTkFzIGFuZCBibGFua3MgY2FuIGJlIGtlcHQgaW4gdGhlIGRhdGFzZXQuDQoNCiMjIENyZWF0ZSBhIG5ldyBjb2x1bW4gY2FsbGVkIERhdGUgdG8gY29tYmluZSB5ZWFyIGFuZCBtb250aA0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbmV3IGNvbHVtbiBEYXRlIHRvIGNvbWJpbmUgeWVhciBhbmQgbW9udGggYXMgRGF0ZXRpbWUgZGF0YXR5cGUNCmRmJERhdGUgPC0gbHVicmlkYXRlOjp5bWQocGFzdGUoZGYkWWVhciwgZGYkTW9udGgsICIwMSIsIHNlcCA9ICItIikpDQpgYGANCg0KQ2hlY2sgY29sdW1uIGRhdGEgdHlwZToNCg0KYGBge3J9DQojIENoZWNrIGRhdGEgdHlwZSBvZiBEYXRlIGNvbHVtbg0KY2xhc3MoZGYkRGF0ZSkNCmBgYA0KDQojIyBTZWxlY3QgYWxsIHRoZSByb3dzIHdoaWNoIGhhdmUgKk5vcm92aXJ1cyogYXMgc3Vic3RyaW5nDQoNCmBgYHtyfQ0KIyBGaWx0ZXIgcm93cyBjb250YWluIE5vcm92aXJ1cyBhcyBzdWJzdHJpbmcNCm5vcm92aXJ1c19kZiA8LSBkZiAlPiUNCiAgZmlsdGVyKHN0cl9kZXRlY3QoZGYkRXRpb2xvZ3ksICJOb3JvdmlydXMiKSkNCmBgYA0KDQojIyBQcmVzZW50IHN0YXRpc3RpY3Mgb24gKk5vcm92aXJ1cyogdGhhdCB5b3UgdGhpbmsgd291bGQgYmUgb2YgaW50ZXJlc3QgdG8geW91ciBwcm9qZWN04oCZcyBzcG9uc29yDQoNCiMjIyBXaGljaCBwcmltYXJ5IG1vZGUgaGFzIHRoZSBoaWdoZXN0IG51bWJlciBvZiAqTm9yb3ZpcnVzKiBvdXRicmVha3M/DQoNCmBgYHtyfQ0KIyBHcm91cCBkYXRhIGJ5IFByaW1hcnlfTW9kZSBjb2x1bW4gYW5kIGNvdW50IHRoZSBudW1iZXIgb2Ygb2NjdXJlbmNlcw0Kbm9yb3ZpcnVzX2J5X3ByaW1hcnlfbW9kZSA8LSBub3JvdmlydXNfZGYgJT4lIA0KICBjb3VudChQcmltYXJ5X01vZGUpDQoNCm5vcm92aXJ1c19ieV9wcmltYXJ5X21vZGUNCmBgYA0KDQpSZXN1bHQ6ICpQZXJzb24tdG8tcGVyc29uKiBpcyB0aGUgbWFpbiByZWFzb24gb2YgKk5vcm92aXJ1cyogb3V0YnJlYWtzLCB3aXRoIDIxMjE0IGNhc2VzLg0KDQojIyMgV2hlcmUgYXJlIHRoZSB0b3AgMyBwbGFjZXMgdGhhdCAqTm9yb3ZpcnVzKiBvdXRicmVha3Mgb2NjdXIgbW9zdCBmcmVxdWVudGx5IHdoZW4gdGhlIHByaW1hcnkgbW9kZSBpcyAqUGVyc29uLXRvLXBlcnNvbio/DQoNCmBgYHtyfQ0KcGVyc29uX3RvX3BlcnNvbl9ieV9zZXR0aW5nIDwtIG5vcm92aXJ1c19kZiAlPiUgDQogICMgU2VsZWN0IFBlcnNvbi10by1wZXJzb24gYXMgdGhlIHByaW1hcnkgbW9kZSBhbmQgZmlsdGVyIG91dCBOQXMgZnJvbSBTZXR0aW5nIGNvbHVtbg0KICBmaWx0ZXIoUHJpbWFyeV9Nb2RlID09ICJQZXJzb24tdG8tcGVyc29uIiwgIWlzLm5hKFNldHRpbmcpKQ0KDQpub3JvdmlydXNfYnlfcGxhY2VzIDwtIHBlcnNvbl90b19wZXJzb25fYnlfc2V0dGluZyAlPiUNCiAgIyBHcm91cCBkYXRhIGJ5IFNldHRpbmcgY29sdW1uIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIG9jY3VyZW5jZXMNCiAgY291bnQoU2V0dGluZykgJT4lDQogIGZpbHRlcihuID4gMCkgJT4lDQogIGFycmFuZ2UoZGVzYyhub3JvdmlydXNfYnlfcGxhY2VzJG4pKQ0KDQpub3JvdmlydXNfYnlfcGxhY2VzIA0KYGBgDQoNClJlc3VsdDogVG9wIDMgcGxhY2VzIHRoYXQgZWFzaWx5IHRyYW5zbWl0IHRoZSAqTm9yb3ZpcnVzKiBhcmUgKkNoaWxkIGRheWNhcmUvcHJlc2Nob29sKiwgKkhvc3BpdGFsKiwgYW5kICpMb25nLXRlcm0gY2FyZS9udXJzaW5nIGhvbWUvYXNzaXN0ZWQgbGl2aW5nIGZhY2lsaXR5Ki4NCg0KIyMgQ3JlYXRlIGdyYXBocyB0byB2aXN1YWxpemUgdGhlIHN0YXRpc3RpY3Mgb2YgKk5vcm92aXJ1cyoNCg0KIyMjIFdoaWNoIHByaW1hcnkgbW9kZSBoYXMgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mICpOb3JvdmlydXMqIG91dGJyZWFrcz8NCg0KYGBge3IgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTN9DQojIENyZWF0ZSBhIGhvcml6b250YWwgYmFyIGNoYXJ0DQpnZ3Bsb3QoDQogIG5vcm92aXJ1c19ieV9wcmltYXJ5X21vZGUsIA0KICBhZXMoeCA9IG4sIHkgPSBQcmltYXJ5X01vZGUpDQopICsNCiAgZ2VvbV9iYXIoDQogICAgc3RhdCA9ICJpZGVudGl0eSIsDQogICAgYWVzKGZpbGwgPSBpZmVsc2UobiA9PSBtYXgobiksIHJfOTAwLCB6XzMwMCkpDQogICkgKyANCiAgZ2VvbV90ZXh0KA0KICAgICMgRGlzcGxheSB0ZXh0IGZvciBvbmx5IHRoZSBiYXIgd2l0aCBoaWdoZXN0IG51bWJlciBvZiBvY2N1cnJlbmNlcw0KICAgIGFlcyhsYWJlbCA9IGlmZWxzZShuID09IG1heChuKSwgbiwgIiIpKSwNCiAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgaGp1c3QgPSAxLjM1LA0KICAgIHZqdXN0ID0gMC4zNQ0KICApICsNCiAgIyBFbmFibGUgY3VzdG9tIGZpbGwgY29sb3INCiAgc2NhbGVfZmlsbF9pZGVudGl0eSgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJOb3JvdmlydXMgb3V0YnJlYWtzIGJ5IHByaW1hcnkgbW9kZSIsDQogICAgeCA9ICJPdXRicmVha3MiDQogICkgKw0KICAjIEFkanVzdCB0aGVtZSBmb3IgYSBiZXR0ZXIgbG9vaw0KICB0aGVtZV9jbGFzc2ljKCkgKw0KICB0aGVtZSgNCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpLA0KICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCgpLA0KICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dCgpLA0KICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKQ0KICApDQpgYGANCg0KIyMjIFdoZXJlIGFyZSB0aGUgdG9wIDMgcGxhY2VzIHRoYXQgKk5vcm92aXJ1cyogb3V0YnJlYWtzIG9jY3VyIG1vc3QgZnJlcXVlbnRseSB3aGVuIHRoZSBwcmltYXJ5IG1vZGUgaXMgKlBlcnNvbi10by1wZXJzb24qPw0KDQpgYGB7ciBmaWcud2lkdGg9MTUsZmlnLmhlaWdodD05fQ0KaXNfdG9wXzNfcGxhY2VzIDwtIGZ1bmN0aW9uKHNldHRpbmcpIHsNCiAgcmV0dXJuKHNldHRpbmcgJWluJSBoZWFkKG5vcm92aXJ1c19ieV9wbGFjZXMsIDMpJFNldHRpbmcNCiAgKQ0KfQ0KDQpnZ3Bsb3QoDQogIG5vcm92aXJ1c19ieV9wbGFjZXMsDQogICMgeC1heGlzIGlzIHRoZSBudW1iZXIgb2Ygb2NjdXJyZW5jZXMsIHktYXhpcyBpcyBzb3J0ZWQgYnkgZGVzY2VuZGluZyBvZiBuDQogIGFlcyh4ID0gbiwgeSA9IHJlb3JkZXIoU2V0dGluZywgbikpDQopICsNCiAgZ2VvbV9iYXIoDQogICAgc3RhdCA9ICJpZGVudGl0eSIsDQogICAgYWVzKGZpbGwgPSBpZmVsc2UoaXNfdG9wXzNfcGxhY2VzKFNldHRpbmcpLCByXzkwMCwgel8zMDApKQ0KICApICsNCiAgZ2VvbV90ZXh0KCAjIHNpemUgaW4gcHQNCiAgICBhZXMobGFiZWwgPSBpZmVsc2UoaXNfdG9wXzNfcGxhY2VzKFNldHRpbmcpLCBuLCAiIikpLA0KICAgIGNvbG9yID0gIndoaXRlIiwNCiAgICBoanVzdCA9IDEuMzUsDQogICAgdmp1c3QgPSAwLjM1DQogICkgKw0KICBzY2FsZV9maWxsX2lkZW50aXR5KCkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlRvcCAzIGxvY2F0aW9ucyBmb3IgTm9yb3ZpcnVzIG91dGJyZWFrcyB2aWEgUGVyc29uLXRvLXBlcnNvbiB0cmFuc21pc3Npb24iLA0KICAgIHggPSAiT3V0YnJlYWtzIg0KICApICsNCiAgIyBBZGp1c3QgdGhlbWUgZm9yIGEgYmV0dGVyIGxvb2sNCiAgdGhlbWVfY2xhc3NpYygpICsNCiAgdGhlbWUoDQogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSwNCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQoKSwNCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoKSwNCiAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCkNCiAgKQ0KYGBgDQoNCiMjIFNlbGVjdCBhbGwgdGhlIHJvd3Mgd2hpY2ggaGF2ZSAqRXNjaGVyaWNoaWEqIGFzIHN1YnN0cmluZw0KDQpgYGB7cn0NCiMgRmlsdGVyIHJvd3MgY29udGFpbiBFc2NoZXJpY2hpYSBhcyBzdWJzdHJpbmcNCmVzY2hlcmljaGlhX2NvbGlfZGYgPC0gZGYgJT4lDQogIGZpbHRlcihzdHJfZGV0ZWN0KGRmJEV0aW9sb2d5LCAiRXNjaGVyaWNoaWEiKSkNCmBgYA0KDQojIyBEZXRlcm1pbmUgaG93IHRvIGhhbmRsZSBpbnN0YW5jZXMgd2hlcmUgbXVsdGlwbGUgZXRpb2xvZ2llcyBhcmUgbGlzdGVkDQoNCkluc3RhbmNlcyB3aXRoIG11bHRpcGxlIGV0aW9sb2dpZXMgY2FuIGJlIHNwbGl0IGludG8gbXVsdGlwbGUgcm93cy4gSG93ZXZlciwgdGhpcyBhcHByb2FjaCBpcyBvbmx5IHN1aXRhYmxlIGZvciBkYXRhIHZpc3VhbGl6YXRpb24gc2NlbmFyaW9zIHdoZW4gb25seSBhIGZldyBjb2x1bW5zIGFyZSBzZWxlY3RlZCB0byBwbG90IGEgZ3JhcGguIEl0IGNhbm5vdCBiZSBhcHBsaWVkIHRvIHRoZSBlbnRpcmUgZGF0YXNldCBiZWNhdXNlIHNvbWUgdmFsdWVzLCBsaWtlIHRoZSBudW1iZXIgb2YgaWxsbmVzc2VzLCBob3NwaXRhbGl6YXRpb25zLCBhbmQgZGVhdGhzLCBjYW5ub3QgYmUgc3BsaXQuDQoNCiMjIFByZXNlbnQgc3RhdGlzdGljcyBvbiAqRXNjaGVyaWNoaWEqIHRoYXQgeW91IHRoaW5rIHdvdWxkIGJlIG9mIGludGVyZXN0IHRvIHlvdXIgcHJvamVjdOKAmXMgc3BvbnNvcg0KDQojIyMgV2hpY2ggcHJpbWFyeSBtb2RlIGhhcyB0aGUgaGlnaGVzdCBudW1iZXIgb2YgKkVzY2hlcmljaGlhKiBvdXRicmVha3M/DQoNCmBgYHtyfQ0KIyBHcm91cCBkYXRhIGJ5IFByaW1hcnlfTW9kZSBjb2x1bW4gYW5kIGNvdW50IHRoZSBudW1iZXIgb2Ygb2NjdXJlbmNlcw0KZXNjaGVyaWNoaWFfY29saV9ieV9wcmltYXJ5X21vZGUgPC0gZXNjaGVyaWNoaWFfY29saV9kZiAlPiUgDQogIGNvdW50KFByaW1hcnlfTW9kZSkNCg0KZXNjaGVyaWNoaWFfY29saV9ieV9wcmltYXJ5X21vZGUNCmBgYA0KDQpSZXN1bHQ6ICpGb29kKiBpcyB0aGUgbWFpbiByZWFzb24gb2YgKkVzY2hlcmljaGlhKiBvdXRicmVha3MsIHdpdGggNzg0IGNhc2VzLg0KDQojIyMgV2hpY2ggZm9vZCB2ZWhpY2xlIGlzIHRoZSBtb3N0IGZyZXF1ZW50bHkgYXNzb2NpYXRlZCB3aXRoICpFc2NoZXJpY2hpYSogb3V0YnJlYWtzIHdoZW4gdGhlIHByaW1hcnkgbW9kZSBpcyAqRm9vZCo/DQoNCmBgYHtyfQ0KZXNjaGVyaWNoaWFfY29saV9mb29kX3ZlaGljbGVzIDwtIGVzY2hlcmljaGlhX2NvbGlfZGYgJT4lIA0KICAjIFNlbGVjdCBGb29kIGFzIHRoZSBwcmltYXJ5IG1vZGUgYW5kIGZpbHRlciBvdXQgTkFzIGZyb20gRm9vZCBWZWhpY2xlIGNvbHVtbg0KICBmaWx0ZXIoUHJpbWFyeV9Nb2RlID09ICJGb29kIiwgIWlzLm5hKEZvb2RfVmVoaWNsZSkpICU+JQ0KICAjIFNwbGl0IG11bHRpcGxlIGZvb2QgdmVoaWNsZXMgaW50byBzZXBhcmF0ZSByb3dzLCB3aXRoIGRlbGltaXRlciBpcyBzZW1pY29sb24gJzsnDQogIHNlcGFyYXRlX2xvbmdlcl9kZWxpbShGb29kX1ZlaGljbGUsIGRlbGltID0gIjsiKSAlPiUNCiAgIyBHcm91cCBkYXRhIGJ5IFNldHRpbmcgY29sdW1uIGFuZCBjb3VudCB0aGUgbnVtYmVyIG9mIG9jY3VyZW5jZXMNCiAgY291bnQoRm9vZF9WZWhpY2xlKSAlPiUNCiAgIyBTb3J0IGJ5IGRlc2NlbmRpbmcgb2Ygbg0KICBhcnJhbmdlKGRlc2MobikpDQoNCmVzY2hlcmljaGlhX2NvbGlfZm9vZF92ZWhpY2xlcw0KYGBgDQoNClJlc3VsdDogKmdyb3VuZCBiZWVmLCBoYW1idXJnZXIqIGlzIHRoZSBtb3N0IGZyZXF1ZW50bHkgYXNzb2NpYXRlZCB3aXRoICpFc2NoZXJpY2hpYSogb3V0YnJlYWtzIHdoZW4gdGhlIHByaW1hcnkgbW9kZSBpcyAqRm9vZCouDQoNCiMjIENyZWF0ZSBncmFwaHMgdG8gdmlzdWFsaXplIHRoZSBzdGF0aXN0aWNzIG9mICpFc2NoZXJpY2hpYSoNCg0KIyMjIFdoaWNoIHByaW1hcnkgbW9kZSBoYXMgdGhlIGhpZ2hlc3QgbnVtYmVyIG9mICpFc2NoZXJpY2hpYSogb3V0YnJlYWtzPw0KDQpgYGB7ciBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9M30NCiMgQ3JlYXRlIGEgaG9yaXpvbnRhbCBiYXIgY2hhcnQNCmdncGxvdCgNCiAgZXNjaGVyaWNoaWFfY29saV9ieV9wcmltYXJ5X21vZGUsIA0KICBhZXMoeCA9IG4sIHkgPSBQcmltYXJ5X01vZGUpDQopICsNCiAgZ2VvbV9iYXIoDQogICAgc3RhdCA9ICJpZGVudGl0eSIsDQogICAgYWVzKGZpbGwgPSBpZmVsc2UobiA9PSBtYXgobiksIHJfOTAwLCB6XzMwMCkpDQogICkgKyANCiAgZ2VvbV90ZXh0KA0KICAgICMgRGlzcGxheSB0ZXh0IGZvciBvbmx5IHRoZSBiYXIgd2l0aCBoaWdoZXN0IG51bWJlciBvZiBvY2N1cnJlbmNlcw0KICAgIGFlcyhsYWJlbCA9IGlmZWxzZShuID09IG1heChuKSwgbiwgIiIpKSwNCiAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgaGp1c3QgPSAxLjM1LA0KICAgIHZqdXN0ID0gMC4zNQ0KICApICsNCiAgIyBFbmFibGUgY3VzdG9tIGZpbGwgY29sb3INCiAgc2NhbGVfZmlsbF9pZGVudGl0eSgpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJFc2NoZXJpY2hpYSBvdXRicmVha3MgYnkgcHJpbWFyeSBtb2RlIiwNCiAgICB4ID0gIk91dGJyZWFrcyINCiAgKSArDQogICMgQWRqdXN0IHRoZW1lIGZvciBhIGJldHRlciBsb29rDQogIHRoZW1lX2NsYXNzaWMoKSArDQogIHRoZW1lKA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksDQogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KCksDQogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLA0KICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KCksDQogICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpDQogICkNCmBgYA0KDQojIyMgV2hpY2ggZm9vZCB2ZWhpY2xlIGlzIHRoZSBtb3N0IGZyZXF1ZW50bHkgYXNzb2NpYXRlZCB3aXRoICpFc2NoZXJpY2hpYSogb3V0YnJlYWtzIHdoZW4gdGhlIHByaW1hcnkgbW9kZSBpcyAqRm9vZCo/DQoNCmBgYHtyIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTEwfQ0KZ2dwbG90KA0KICBlc2NoZXJpY2hpYV9jb2xpX2Zvb2RfdmVoaWNsZXMsIA0KICBhZXMobGFiZWwgPSBGb29kX1ZlaGljbGUsIHNpemUgPSBuLCBjb2xvciA9IG4pKSArDQogIGdlb21fdGV4dF93b3JkY2xvdWQoKSArDQogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDMwKSArDQogIHRoZW1lX21pbmltYWwoKSArDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50KGxvdyA9IHpfNTAwLCBoaWdoID0gcl85MDApDQpgYGANCg0KTm90aWNlIHRoYXQgdG9wIDMgd29yZHMgYXJlIGhhdmluZyAqZ3JvdW5kIGJlZWYqIGFzIHN1YnN0cmluZyBpbiB0aGUgZ3JhcGguDQoNCiMjIFNlbGVjdCBhdCBsZWFzdCBvbmUgc3RhdGlzdGljIGFuZCBwcmVzZW50IGEgZ3JhcGggc2hvd2luZyBib3RoIEVzY2hlcmljaGlhIGFuZCBOb3JvdmlydXMgdG9nZXRoZXINCg0KYGBge3J9DQpzcGxpdF9ldGlvbG9neSA8LSBkZiAlPiUNCiAgIyBGaWx0ZXIgRXRpb2xvZ3kgd2l0aG91dCBOQXMNCiAgZmlsdGVyKCFpcy5uYShFdGlvbG9neSkpICU+JQ0KICAjIFNwbGl0IG11bHRpcGxlIEV0aW9sb2d5IGludG8gc2VwYXJhdGUgcm93cywgd2l0aCBkZWxpbWl0ZXIgaXMgc2VtaWNvbG9uICc7Jw0KICBzZXBhcmF0ZV9sb25nZXJfZGVsaW0oRXRpb2xvZ3ksIGRlbGltID0gIjsiKQ0KDQojIFJlbmFtZSB0aGUgZXRpb2xvZ2llcyBmb3IgZ3JvdXBpbmcgbGF0ZXINCnNwbGl0X2V0aW9sb2d5JEV0aW9sb2d5IDwtIHN0cl9yZXBsYWNlKHNwbGl0X2V0aW9sb2d5JEV0aW9sb2d5LCAiLipOb3JvdmlydXMuKiIsICJOb3JvdmlydXMiKQ0Kc3BsaXRfZXRpb2xvZ3kkRXRpb2xvZ3kgPC0gc3RyX3JlcGxhY2Uoc3BsaXRfZXRpb2xvZ3kkRXRpb2xvZ3ksICIuKkVzY2hlcmljaGlhLioiLCAiRXNjaGVyaWNoaWEiKQ0KDQojIEFkZCBzdGF0ZSBjb2RlIGNvbHVtbiBmb3IgcGxvdHRpbmcgY2hvcm9wbGV0aCBtYXANCnNwbGl0X2V0aW9sb2d5JFN0YXRlX0NvZGUgPC0gc3RhdGUuYWJiW21hdGNoKHNwbGl0X2V0aW9sb2d5JFN0YXRlLCBzdGF0ZS5uYW1lKV0NCg0KIyBGaWx0ZXIgTm9yb3ZpcnVzIGFuZCBFc2NoZXJpY2hpYSBldGlvbG9naWVzIG9ubHkNCm5vcm92aXJ1c19hbmRfZXNjaGVyaWNoaWEgPC0gc3BsaXRfZXRpb2xvZ3kgJT4lDQogIGZpbHRlcihFdGlvbG9neSA9PSAiTm9yb3ZpcnVzIiB8IEV0aW9sb2d5ID09ICJFc2NoZXJpY2hpYSIpDQpgYGANCg0KDQojIyMgVGhlIHRvdGFsIG51bWJlciBvZiBzaWNrZW5lZCAoaWxsbmVzc2VzLCBob3NwaXRhbGl6YXRpb25zICYgZGVhdGhzKSBvZiBOb3JvdmlydXMgYW5kIEVzY2hlcmljaGlhIGV0aW9sb2dpZXMgYnkgeWVhcg0KDQpgYGB7cn0NCm5vcm92aXJ1c19hbmRfZXNjaGVyaWNoaWFfYnlfeWVhciA8LSBub3JvdmlydXNfYW5kX2VzY2hlcmljaGlhICU+JQ0KICAjIFNlbGVjdCBZZWFyIGFuZCBFdGlvbG9neQ0KICBzZWxlY3QoWWVhciwgRXRpb2xvZ3ksIElsbG5lc3NlcywgSG9zcGl0YWxpemF0aW9ucywgRGVhdGhzKSAlPiUNCiAgZ3JvdXBfYnkoWWVhciwgRXRpb2xvZ3kpICU+JQ0KICBzdW1tYXJpemUoDQogICAgIyBDYWxjdWxhdGUgc3VtIG9mIGlsbG5lc3NlcywgaG9zcGl0YWxpemF0aW9ucyAmIGRlYXRocywgaWdub3JpbmcgTkFzDQogICAgVG90YWxfU2lja2VuZWQgPSBzdW0oSWxsbmVzc2VzICsgSG9zcGl0YWxpemF0aW9ucyArIERlYXRocywgbmEucm0gPSBUUlVFKQ0KICApICU+JQ0KICBmaWx0ZXIoVG90YWxfU2lja2VuZWQgPiAwKQ0KDQpub3JvdmlydXNfYW5kX2VzY2hlcmljaGlhX2J5X3llYXINCmBgYA0KDQoNCmBgYHtyIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTV9DQpnZ3Bsb3QoDQogIG5vcm92aXJ1c19hbmRfZXNjaGVyaWNoaWFfYnlfeWVhciwgDQogIGFlcyh4ID0gWWVhciwgeSA9IFRvdGFsX1NpY2tlbmVkLCANCiAgICAgIGNvbG9yID0gRXRpb2xvZ3ksDQogICAgICBncm91cCA9IEV0aW9sb2d5DQogICkNCikgKw0KICBnZW9tX2xpbmUoc2l6ZSA9IDEpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWVzKHNoYXBlPUV0aW9sb2d5KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiTm9yb3ZpcnVzIiA9IHJfNzAwLCAiRXNjaGVyaWNoaWEiID0gYl83MDApKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiVG90YWwgbnVtYmVyIG9mIHNpY2tlbmVkIChpbGxuZXNzZXMsIGhvc3BpdGFsaXphdGlvbnMgJiBkZWF0aHMpIG9mIE5vcm92aXJ1cyBhbmQgRXNjaGVyaWNoaWEgZXRpb2xvZ2llcyBieSB5ZWFyIiwNCiAgICB4ID0gIlllYXIiLA0KICAgIHkgPSAiVG90YWwgbnVtYmVyIG9mIHNpY2tlbmVkIg0KICApICsNCiAgdGhlbWVfY2xhc3NpYygpDQpgYGANCg0KIyMjIE5vcm92aXJ1cyBhbmQgRXNjaGVyaWNoaWEgZXRpb2xvZ2llcyBvdXRicmVha3MgYnkgc3RhdGUNCg0KYGBge3J9DQpub3JvdmlydXNfYW5kX2VzY2hlcmljaGlhX2J5X3N0YXRlIDwtIG5vcm92aXJ1c19hbmRfZXNjaGVyaWNoaWEgJT4lDQogICMgU2VsZWN0IFllYXIgYW5kIEV0aW9sb2d5DQogIHNlbGVjdChTdGF0ZSwgU3RhdGVfQ29kZSwgRXRpb2xvZ3ksIElsbG5lc3NlcywgSG9zcGl0YWxpemF0aW9ucywgRGVhdGhzKSAlPiUNCiAgZ3JvdXBfYnkoU3RhdGVfQ29kZSwgRXRpb2xvZ3kpICU+JQ0KICBzdW1tYXJpemUoDQogICAgIyBDYWxjdWxhdGUgc3VtIG9mIGlsbG5lc3NlcywgaG9zcGl0YWxpemF0aW9ucyAmIGRlYXRocywgaWdub3JpbmcgTkFzDQogICAgVG90YWxfU2lja2VuZWQgPSBzdW0oSWxsbmVzc2VzICsgSG9zcGl0YWxpemF0aW9ucyArIERlYXRocywgbmEucm0gPSBUUlVFKQ0KICApICU+JQ0KICBmaWx0ZXIoVG90YWxfU2lja2VuZWQgPiAwKQ0KDQpub3JvdmlydXNfYW5kX2VzY2hlcmljaGlhX2J5X3N0YXRlDQpgYGANCg0KYGBge3J9DQpub3JvdmlydXNfYW5kX2VzY2hlcmljaGlhX2J5X3N0YXRlICU+JQ0KICBmaWx0ZXIoRXRpb2xvZ3kgPT0gIk5vcm92aXJ1cyIpICU+JQ0KICBwbG90X2dlbyhsb2NhdGlvbm1vZGUgPSAnVVNBLXN0YXRlcycpICU+JQ0KICAjIEJ1aWxkIHRoZSBtYXANCiAgYWRkX3RyYWNlKA0KICAgIHR5cGU9ImNob3JvcGxldGgiLA0KICAgIHogPSB+VG90YWxfU2lja2VuZWQsDQogICAgbG9jYXRpb25zID0gflN0YXRlX0NvZGUsDQogICAgY29sb3IgICAgID0gflRvdGFsX1NpY2tlbmVkLA0KICAgIGNvbG9yc2NhbGUgPSBsaXN0KA0KICAgICAgIyBMb3cgbGV2ZWwNCiAgICAgIGMoMCwgIiNmZWUyZTIiKSwNCiAgICAgIGMoMC41LCAiI2VmNDQ0NCIpLA0KICAgICAgYygxLCByXzkwMCkNCiAgICApDQogICkgJT4lDQogICMgaHR0cHM6Ly9wbG90bHkuY29tL3IvcmVmZXJlbmNlL2xheW91dC9nZW8NCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gJ05vcm92aXJ1cyBldGlvbG9naWVzIG91dGJyZWFrcyBieSBzdGF0ZScsDQogICAgZ2VvID0gbGlzdCgNCiAgICAgICMgU2hvdyBVU0EgaW5zdGVhZCBvZiB0aGUgd29ybGQgbWFwDQogICAgICBzY29wZSAgICAgID0gInVzYSIsDQogICAgICAjIE1hcCBkYXRhIGFjcm9zcyB0aGUgc3RhdGVzDQogICAgICBwcm9qZWN0aW9uID0gbGlzdCh0eXBlID0gImFsYmVycyB1c2EiKQ0KICAgICkNCiAgKSAlPiUNCiAgIyBDb2xvciBiYXIgdGl0bGUNCiAgY29sb3JiYXIodGl0bGUgPSAiVG90YWwgbnVtYmVyIG9mIHNpY2tlbmVkIikNCmBgYA0KDQpgYGB7cn0NCm5vcm92aXJ1c19hbmRfZXNjaGVyaWNoaWFfYnlfc3RhdGUgJT4lDQogIGZpbHRlcihFdGlvbG9neSA9PSAiRXNjaGVyaWNoaWEiKSAlPiUNCiAgcGxvdF9nZW8obG9jYXRpb25tb2RlID0gJ1VTQS1zdGF0ZXMnKSAlPiUNCiAgIyBCdWlsZCB0aGUgbWFwDQogIGFkZF90cmFjZSgNCiAgICB0eXBlPSJjaG9yb3BsZXRoIiwNCiAgICB6ID0gflRvdGFsX1NpY2tlbmVkLA0KICAgIGxvY2F0aW9ucyA9IH5TdGF0ZV9Db2RlLA0KICAgIGNvbG9yICAgICA9IH5Ub3RhbF9TaWNrZW5lZCwNCiAgICBjb2xvcnNjYWxlID0gbGlzdCgNCiAgICAgICMgTG93IGxldmVsDQogICAgICBjKDAsIGJfMTAwKSwNCiAgICAgIGMoMC41LCBiXzUwMCksDQogICAgICBjKDEsIGJfOTAwKQ0KICAgICkNCiAgKSAlPiUNCiAgIyBodHRwczovL3Bsb3RseS5jb20vci9yZWZlcmVuY2UvbGF5b3V0L2dlbw0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAnRXNjaGVyaWNoaWEgZXRpb2xvZ2llcyBvdXRicmVha3MgYnkgc3RhdGUnLA0KICAgIGdlbyA9IGxpc3QoDQogICAgICAjIFNob3cgVVNBIGluc3RlYWQgb2YgdGhlIHdvcmxkIG1hcA0KICAgICAgc2NvcGUgICAgICA9ICJ1c2EiLA0KICAgICAgIyBNYXAgZGF0YSBhY3Jvc3MgdGhlIHN0YXRlcw0KICAgICAgcHJvamVjdGlvbiA9IGxpc3QodHlwZSA9ICJhbGJlcnMgdXNhIikNCiAgICApDQogICkgJT4lDQogICMgQ29sb3IgYmFyIHRpdGxlDQogIGNvbG9yYmFyKHRpdGxlID0gIlRvdGFsIG51bWJlciBvZiBzaWNrZW5lZCIpDQpgYGANCg0KDQoNCg==